package org.fuys.coder.domain.audit.service.policy.audit.impl;

import org.fuys.coder.domain.config.service.impl.CoderAuditConfig;
import org.fuys.coder.domain.audit.model.event.AsyncAuditEvent;
import org.fuys.coder.domain.audit.model.event.AsyncTaskIdHolder;
import org.fuys.coder.domain.audit.model.event.AsyncTasksOverEvent;
import org.fuys.coder.domain.audit.model.req.AuditTask;
import org.fuys.coder.domain.audit.model.res.AuditResult;
import org.fuys.coder.domain.audit.model.vo.AuditResultTypeVO;
import org.fuys.coder.domain.audit.model.vo.AuditTaskTypeVO;
import org.fuys.coder.domain.audit.service.policy.audit.AuditStrategy;
import org.fuys.coder.domain.audit.service.publisher.AsyncAuditEventPublisher;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @projectName: fuys-low-coder
 * @package: org.fuys.coder.domain.audit.service.policy.audit
 * @className: SubstanceAuditStrategy
 * @author: WangJing
 * @description: 内容审核策略
 * @date: 2024/6/22 17:20
 * @version: 1.0
 */
@Service("multipleAuditStrategy")
public class MultipleAuditStrategy implements AuditStrategy {

    @Resource
    private ImageAuditStrategy imageAuditStrategy;

    @Resource
    private TextAuditStrategy textAuditStrategy;

    @Resource
    private CoderAuditConfig config;

    @Resource
    private AsyncAuditEventPublisher publisher;

    private ExecutorService threadPoolExecutor;


    @PostConstruct
    public void initAuditStrategy(){
        threadPoolExecutor= Executors.newFixedThreadPool(config.getSubstanceAuditCores());
    }

    @Override
    public AuditResult execute(AuditTask task) {
        final String[] strings = task.getTasks();
        //这里对于内容的审核 则是根据|进行切分 这也没办法 只能使用这种方式来完成策略模式了
        List<String> textList=new ArrayList<>();
        List<String> imageList=new ArrayList<>();
        boolean flag=false;
        for (String string : strings) {
            if(!flag){
                //如果没发现"|" 则说明是文本 一般可能是 简介 标题 内容等
                if(string.equals("|")){
                    flag=true;
                    continue;
                }
                textList.add(string);
            }else{
                imageList.add(string);
            }
        }
        //构造审核结果
        AuditResult auditResult=AuditResult.normal();
        threadPoolExecutor.submit(()->{
            doAudit(textList,imageList,task);
        });
        return null;
    }

    private void doAudit(List<String> textList,List<String> imageList,AuditTask multipleTask){
        //这一步将记录每个内容对应的需要异步审核的数量 当在上下文中发现一个任务id对应的异步审核任务完成的数量和记录所需要的数量匹配
        //则发送事件 这里这个内容的事件监听器可以获取到这个事件 从而达成解耦操作
        //具体来说 在上下文中 如果发现一个任务id对应的异步审核任务完成的数量和记录所需要的数量匹配
        //则 上下文发布合并审核结果事件 然后 由多种审核类型的策略监听此事件并处理(因为一般要不是个人名片 要不就是内容 只有这两个是多种审核的)
        //由于在此之前 这里已经拿到了这些审核结果的引用 因此可以直接进行合并操作
        final String taskId = UUID.randomUUID().toString();
        AsyncTaskIdHolder.setTaskInfo(taskId,multipleTask);
        List<AuditResult> auditResults=new LinkedList<>();
        for (String s : textList) {
            AuditTask auditTask=new AuditTask();
            auditTask.setTasks(new String[]{s});
            auditTask.setType(AuditTaskTypeVO.TEXT.getIndex());
            auditTask.setUserId(multipleTask.getUserId());
            auditTask.setOtherId(multipleTask.getOtherId());
            auditTask.setIdType(multipleTask.getIdType());
            auditTask.setNeedResultHandle(false);
            final AuditResult execute = textAuditStrategy.execute(auditTask);
            auditResults.add(execute);
        }
        for (String s : imageList) {
            AuditTask auditTask=new AuditTask();
            auditTask.setTasks(new String[]{s, taskId});
            auditTask.setType(AuditTaskTypeVO.IMAGE.getIndex());
            auditTask.setUserId(multipleTask.getUserId());
            auditTask.setOtherId(multipleTask.getOtherId());
            auditTask.setIdType(multipleTask.getIdType());
            auditTask.setNeedResultHandle(false);
            final AuditResult execute = imageAuditStrategy.execute(auditTask);
            auditResults.add(execute);
        }
        AsyncTaskIdHolder.setTasks(taskId, auditResults,imageList.size());
    }

    private AuditResult summary(String taskId,List<AuditResult> auditResults){
        AsyncTaskIdHolder.removeMultipleTask(taskId);
        //注意 这里不传递任务id了 因为到这一步代表着多种审核任务已经完成 不属于某个任务的子任务了
        final AuditResult normal = AuditResult.normal();
        auditResults.forEach(auditResult -> {
            //如果不是通过状态 说明肯定是哪里存在问题
            if(auditResult.getType()==AuditResultTypeVO.REVIEW.getIndex()){
                //todo 这里需要人工复检 因此扔到对应的数据库表 但是我现在还没有建好

            }
            if(auditResult.getType()!=AuditResultTypeVO.BLOCK.getIndex()){
                //todo 这里说明审核不通过 记录全部的违规信息 扔到对应的审核记录表 其实可以直接通过收件箱？
            }
        });
        return normal;
    }

    @Component
    public class AsyncTasksOverListener implements ApplicationListener<AsyncTasksOverEvent> {

        @Override
        public void onApplicationEvent(AsyncTasksOverEvent event) {
            final String taskId = event.getTaskId();
            final List<AuditResult> tasks = AsyncTaskIdHolder.getTasks(taskId);
            final AuditResult summary = summary(taskId,tasks);
            final AsyncAuditEvent okEvent = new AsyncAuditEvent(this);
            okEvent.setAuditResult(summary);
            okEvent.setAuditTask(AsyncTaskIdHolder.getTaskInfo(taskId));
            AsyncTaskIdHolder.removeMultipleTask(taskId);
            publisher.publishAuditEvent(okEvent);
        }
    }
}
